﻿
using OnlineMealOrdering.Domain.Base;
using OnlineMealOrdering.Persistence;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Transactions;

namespace OnlineMealOrdering.Persistence
{
    /// <summary>
    /// Domain context implementation with Entity Framework technology.
    /// </summary>
    public class OnlineStoreDomainContext : DomainContext, IDisposable
    {
        public static IDomainContext Current
        {
            get { return OnlineStoreDomainContext.Current; }
        }

        private OnlineStoreDbContext _dbContext;
        protected OnlineStoreDbContext DbContext
        {
            get { return _dbContext; }
        }

        private TransactionScope _transactionScope;

        public OnlineStoreDomainContext()
        {
            _dbContext = new OnlineStoreDbContext();
            DomainManager.RegisterContext<IDomainContext>(this);
        }

        public OnlineStoreDomainContext(int userId)
        {
            _dbContext = new OnlineStoreDbContext(userId);
            DomainManager.RegisterContext<IDomainContext>(this);
        }

        /// <summary>
        /// Create DomainContext within transaction
        /// </summary>
        /// <param name="dbContextConstructor">dbContext constructor</param>
        /// <param name="IsTransactionRequired">indicate if the transaction is required</param>
        public OnlineStoreDomainContext(Func<OnlineStoreDbContext> dbContextConstructor, bool IsTransactionRequired)
        {
            if (IsTransactionRequired)
                _transactionScope = new TransactionScope();

            _dbContext = dbContextConstructor.Invoke();
            DomainManager.RegisterContext<IDomainContext>(this);
        }

        /// <summary>
        /// Add entity into the context
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <param name="entity">entity instance</param>
        /// <returns>entity instance of added</returns>
        public override TEntity Add<TEntity>(TEntity entity)
        {
            return _dbContext.Set<TEntity>().Add(entity);
        }

        /// <summary>
        /// Explicitly indicate that the entity has been changed
        /// </summary>
        /// <typeparam name="TEntity">entity type</typeparam>
        /// <param name="entity">entity instance</param>
        public virtual void Modify<TEntity>(TEntity entity)
            where TEntity : class
        {
            _dbContext.Entry<TEntity>(entity).State = EntityState.Modified;
        }

        /// <summary>
        /// Remove entity from the context
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <param name="entity">entity instance</param>
        public override void Remove<TEntity>(TEntity entity)
        {
            _dbContext.Set<TEntity>().Remove(entity);
        }

        /// <summary>
        /// Obtain a set of entities that support LinQ to SQL syntax.   
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <returns>Querable entities</returns>
        public override IQueryable<TEntity> EntitySet<TEntity>()
        {
            return _dbContext.Set<TEntity>();
        }

        /// <summary>
        /// Obtain a set of filtered entities that supports LinQ to SQL statement.
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <param name="predicate">Filter condition</param>
        /// <returns>Querable entities</returns>
        public override IQueryable<TEntity> EntitySet<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
        {
            return _dbContext.Set<TEntity>().Where(predicate);
        }

        /// <summary>
        /// Find entity by entity key that can be a key or a composite key
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        /// <param name="keyValues">Key or composite key</param>
        /// <returns>Entity instance or null</returns>
        public override TEntity Find<TEntity>(params object[] keyValues)
        {
            return _dbContext.Set<TEntity>().Find(keyValues);
        }

        /// <summary>
        /// Submit current entity changes to database
        /// </summary>
        public override void SubmitChanges()
        {
            _dbContext.SaveChanges();
        }

        /// <summary>
        /// Dispose 
        /// </summary>
        public virtual void Dispose()
        {
            _dbContext.Dispose();

            //If the transaction is required, complete and dispose the transaction.
            if (_transactionScope != null)
            {
                try
                {
                    _transactionScope.Complete();
                }
                finally
                {
                    _transactionScope.Dispose();
                }
            }

            DomainManager.UnregisterContext<IDomainContext>();
        }
    }
}
